-- CIS 552, Mini-project
-- by Mike Gottlieb and Reilly Grant

-- |Main loop for the game.
module Main where

import qualified Card
import qualified Deck
import qualified Poker
import qualified Player
import qualified User
import System.Console.Readline
import System.Exit
import Control.Monad.State

type PokerState a = StateT Poker.GameState IO a

-- |Deals to players and saves the state
deal :: PokerState ()
deal = modify Poker.deal

-- |Subtracts the ante amount from each player and save the state
ante :: PokerState ()
ante = do modify Poker.ante
          postAntePlayers <- players
          liftIO $ User.notifyDestitute postAntePlayers

-- |Starts in a pre-bet state and loops through the players until someone
--  calls.
bettingRound :: PokerState ()
bettingRound =
    do liftIO $ User.clearScreen
--       printState
       printInfo
       action <- promptUser
       performAction action
       gp <- gamePhase
       case gp of
         Poker.Draw  -> return ()
         Poker.Final -> return ()
         _           -> bettingRound
       
-- |Cycles through all players allowing them to discard.
discardRound :: PokerState ()
discardRound = do nPlayers <- activePlayers
                  replicateM_ nPlayers discardRound'
                  leavingDraw
    where discardRound' = do liftIO $ User.clearScreen
--                             printState
                             printInfo
                             action <- promptUser
                             performAction action

-- |Handle the non-GameState related results from promptUser.
promptUser :: PokerState Poker.Action
promptUser = do action <- liftIO User.promptUser
                error <- validateAction action
                validAction <- case error of
                                 Nothing -> return action
                                 Just error -> do liftIO $ putStr error
                                                  newAction <- promptUser
                                                  return newAction
                case validAction of
                  Poker.Help -> do liftIO User.printHelp
                                   newAction <- promptUser
                                   return newAction
                  Poker.Exit -> do liftIO $ exitWith ExitSuccess
                                   return validAction
                  a          -> return a

-- |Get the current player out of the state.
currentPlayer :: PokerState Player.T
currentPlayer = gets Poker.getCurrentPlayer

-- |Get all the players out of the state.
players :: PokerState [Player.T]
players = gets Poker.players

-- |Get the current game phase out of the state.
gamePhase :: PokerState Poker.GamePhase
gamePhase = gets Poker.phase

-- |Get the number of active players (haven't folded).
activePlayers :: PokerState Int
activePlayers = gets Poker.activePlayers

-- |Set the current game phase.
leavingDraw :: PokerState ()
leavingDraw = modify Poker.leavingDraw

-- |Validate an action in the current game state.
validateAction :: Poker.Action -> PokerState (Maybe String)
validateAction a = gets $ Poker.validateAction a

-- |Perform an action on the current game state.
performAction :: Poker.Action -> PokerState ()
performAction a = modify $ Poker.performAction a

-- |Pick the winner of the current round.
pickWinner :: PokerState ()
pickWinner = modify Poker.pickWinner

-- |Reset all the players for the next round.
reset :: PokerState ()
reset = modify Poker.reset

-- |Show information for the player about the current state of the game.
printInfo :: PokerState ()
printInfo = do state <- gets id
               liftIO $ User.gameInfo state
               liftIO $ User.playerInfo (Poker.getCurrentPlayer state)
                          (Poker.possibleActions (Poker.phase state))

-- |Print out all the state, it's ugly, but good for debugging.
printState :: PokerState ()
printState = do state <- gets id
                liftIO $ putStr (show state)

-- |Plays rounds of poker until exited.
gameLoop :: PokerState ()
gameLoop = do ante
              deal
              bettingRound
              gp <- gamePhase
              if (gp /= Poker.Final)
               then do discardRound
                       bettingRound
               else return ()
              pickWinner
              gameState <- gets id
              continue <- liftIO $ User.endGame gameState
              if continue then do reset
                                  gameLoop 
                          else return ()

-- |Gets the players and then begins the main game loop.
main = do players <- User.getPlayers
          if (length players) < 2
             then do putStr "Not enough players.\n"
                     exitWith ExitSuccess
             else return ()
          gameStart <- Poker.newGame players
          gameEnd <- execStateT gameLoop gameStart
          putStr "Game over!\n"

-- |Runs the tests in other modules.
test = do Card.test
          Deck.test
          Poker.test
